/*
Copyright 2011 Raymond Giorgi

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

   http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
 */

package edu.pitt.cs3530.configuration;

import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;

public class FrontEndConfigrationHandler implements ContentHandler {

	public class QosConfiguration {
		private String className;
		private String shortName;
		private boolean isDefault;
		private Map<String, String> parameterMap;

		private QosConfiguration() {
			parameterMap = new HashMap<String, String>();
		}

		public String getClassName() {
			return className;
		}

		public String getShortName() {
			return shortName;
		}

		public boolean isDefault() {
			return isDefault;
		}

		public Map<String, String> getParameterMap() {
			return parameterMap;
		}
	}

	public class PredictorConfiguration {
		private String className;
		private String shortName;
		private long windowTime;
		private long predictionTime;
		private int numberOfIntervals;
		private long sleepTime;
		private String qosName;
		private boolean isDefault;
		private Map<String, String> parameterMap;

		private PredictorConfiguration() {
			parameterMap = new HashMap<String, String>();
		}

		public Map<String, String> getParameterMap() {
			return parameterMap;
		}

		public String getClassName() {
			return className;
		}

		public String getShortName() {
			return shortName;
		}

		public long getWindowTime() {
			return windowTime;
		}

		public long getPredictionTime() {
			return predictionTime;
		}

		public int getNumberOfIntervals() {
			return numberOfIntervals;
		}

		public long getSleepTime() {
			return sleepTime;
		}

		public String getQosName() {
			return qosName;
		}

		public boolean isDefault() {
			return isDefault;
		}
	}

	public class ReactorConfiguration {
		private String className;
		private String shortName;
		private long deadline;
		private long early;
		private long oldTime;
		private long sleepTime;
		private String qosName;
		private boolean isDefault;
		private Map<String, String> parameterMap;

		private ReactorConfiguration() {
			parameterMap = new HashMap<String, String>();
		}

		public Map<String, String> getParameterMap() {
			return parameterMap;
		}

		public String getClassName() {
			return className;
		}

		public String getShortName() {
			return shortName;
		}

		public long getDeadline() {
			return deadline;
		}

		public long getEarly() {
			return early;
		}

		public long getWindowTime() {
			return oldTime;
		}

		public long getSleepTime() {
			return sleepTime;
		}

		public String getQosName() {
			return qosName;
		}

		public boolean isDefault() {
			return isDefault;
		}
	}

	public class ControllerConfiguration {
		private String className;
		private String shortName;
		private double upperThreshold;
		private double lowerThreshold;
		private long timeToScaleUp;
		private long timeToScaleDown;
		private long timeToTurnOn;
		private long timeToTurnOff;
		private boolean isDefault;
		private List<String> listenTos;
		private Map<String, String> parameterMap;

		private ControllerConfiguration() {
			listenTos = new LinkedList<String>();
			parameterMap = new HashMap<String, String>();
		}

		public String getShortName() {
			return shortName;
		}
		
		public boolean isDefault() {
			return isDefault;
		}

		public String getClassName() {
			return className;
		}

		public double getUpperThreshold() {
			return upperThreshold;
		}

		public double getLowerThreshold() {
			return lowerThreshold;
		}

		public long getTimeToScaleUp() {
			return timeToScaleUp;
		}

		public long getTimeToScaleDown() {
			return timeToScaleDown;
		}

		public long getTimeToTurnOn() {
			return timeToTurnOn;
		}

		public long getTimeToTurnOff() {
			return timeToTurnOff;
		}

		public Map<String, String> getParameterMap() {
			return parameterMap;
		}

		public List<String> listenTos() {
			return listenTos;
		}
	}

	private enum State {
		INIT, QOS, PRED, REAC, CTRL
	}

	public class ConfigurationObject {
		private int connectingPort;
		private int utilizationToKeep;
		private long timeToKeepRequests;
		private double utilizationUpperBound;
		private double utilizationLowerBound;
		private double frequencyScaleFactor;
		private String frontEndHostName;
		private String temporaryDirectory;
		private String wakePrefix;
		private String loadBalancerFactoryDirectory;
		private long reactiveScaleDownTime;
		private List<QosConfiguration> qosConfigurations;
		private List<PredictorConfiguration> predictorConfigurations;
		private List<ReactorConfiguration> reactorConfigurations;
		private List<ControllerConfiguration> controllerConfigurations;

		private ConfigurationObject() {
			qosConfigurations = new LinkedList<QosConfiguration>();
			predictorConfigurations = new LinkedList<PredictorConfiguration>();
			reactorConfigurations = new LinkedList<ReactorConfiguration>();
			controllerConfigurations = new LinkedList<ControllerConfiguration>();
		}
		
		public List<ControllerConfiguration> getControllerConfigurations() {
			return controllerConfigurations;
		}

		public List<QosConfiguration> getQosConfiguration() {
			return qosConfigurations;
		}

		public List<PredictorConfiguration> getPredictorConfigurations() {
			return predictorConfigurations;
		}

		public List<ReactorConfiguration> getReactorConfigurations() {
			return reactorConfigurations;
		}

		public long getReactiveScaleDownTime() {
			return reactiveScaleDownTime;
		}

		public int getConnectingPort() {
			return connectingPort;
		}

		public int getUtilizationToKeep() {
			return utilizationToKeep;
		}

		public long getTimeToKeepRequests() {
			return timeToKeepRequests;
		}

		public double getUtilizationUpperBound() {
			return utilizationUpperBound;
		}

		public double getUtilizationLowerBound() {
			return utilizationLowerBound;
		}

		public double getFrequencyScaleFactor() {
			return frequencyScaleFactor;
		}

		public String getFrontEndHostName() {
			return frontEndHostName;
		}

		public String getTemporaryDirectory() {
			return temporaryDirectory;
		}

		public String getWakePrefix() {
			return wakePrefix;
		}

		public String getLoadBalancerFactoryDirectory() {
			return loadBalancerFactoryDirectory;
		}
	}

	private State state = State.INIT;
	private ConfigurationObject obj = new ConfigurationObject();
	private PredictorConfiguration currentPredictor = null;
	private ReactorConfiguration currentReactor = null;
	private ControllerConfiguration currentController = null;

	public ConfigurationObject getConfigurationObject() {
		return obj;
	}

	@Override
	public void characters(char[] arg0, int arg1, int arg2) throws SAXException {
	}

	@Override
	public void endDocument() throws SAXException {
	}

	@Override
	public void endElement(String arg0, String arg1, String arg2)
			throws SAXException {
		if (arg2.equals("predictor")) {
			state = State.INIT;
			obj.predictorConfigurations.add(currentPredictor);
			currentPredictor = null;
		} else if (arg2.equals("reactor")) {
			state = State.INIT;
			obj.reactorConfigurations.add(currentReactor);
			currentReactor = null;
		} else if (arg2.equals("controller")) {
			state = State.INIT;
			obj.controllerConfigurations.add(currentController);
			currentController = null;
		}
	}

	@Override
	public void endPrefixMapping(String arg0) throws SAXException {
	}

	@Override
	public void ignorableWhitespace(char[] arg0, int arg1, int arg2)
			throws SAXException {
	}

	@Override
	public void processingInstruction(String arg0, String arg1)
			throws SAXException {
	}

	@Override
	public void setDocumentLocator(Locator arg0) {
	}

	@Override
	public void skippedEntity(String arg0) throws SAXException {
	}

	@Override
	public void startDocument() throws SAXException {
	}

	@Override
	public void startElement(String arg0, String arg1, String arg2,
			Attributes arg3) throws SAXException {
		String value = arg3.getValue("value");
		if (state == State.INIT) {
			if (arg2.equals("connecting-port")) {
				obj.connectingPort = Integer.parseInt(value);
			} else if (arg2.equals("utilization-to-keep")) {
				obj.utilizationToKeep = Integer.parseInt(value);
			} else if (arg2.equals("time-to-keep-requests")) {
				obj.timeToKeepRequests = Long.parseLong(value);
			} else if (arg2.equals("utilization-upper-bound")) {
				obj.utilizationLowerBound = Double.parseDouble(value);
			} else if (arg2.equals("utilization-lower-bound")) {
				obj.utilizationLowerBound = Double.parseDouble(value);
			} else if (arg2.equals("frequency-scale-factor")) {
				obj.frequencyScaleFactor = Double.parseDouble(value);
			} else if (arg2.equals("front-end-host-name")) {
				obj.frontEndHostName = value;
			} else if (arg2.equals("temporary-directory")) {
				obj.temporaryDirectory = value;
			} else if (arg2.equals("wake-prefix")) {
				obj.wakePrefix = value;
			} else if (arg2.equals("load-balance-factor-directory")) {
				obj.loadBalancerFactoryDirectory = value;
			} else if (arg2.equals("predictor")) {
				state = State.PRED;
				currentPredictor = new PredictorConfiguration();
				currentPredictor.className = arg3.getValue("class");
				currentPredictor.shortName = arg3.getValue("name");
				// a null value here will be evaluated to 'false'
				currentPredictor.isDefault = Boolean.parseBoolean(arg3
						.getValue("default"));
			} else if (arg2.equals("reactor")) {
				state = State.REAC;
				currentReactor = new ReactorConfiguration();
				currentReactor.className = arg3.getValue("class");
				currentReactor.shortName = arg3.getValue("name");
				// a null value here will be evaluated to 'false'
				currentReactor.isDefault = Boolean.parseBoolean(arg3
						.getValue("default"));
			} else if (arg2.equals("controller")) {
				state = State.CTRL;
				currentController = new ControllerConfiguration();
				currentController.className = arg3.getValue("class");
				currentController.shortName = arg3.getValue("name");
				currentController.isDefault = Boolean.parseBoolean(arg3.getValue("default"));
			}
		} else if (state == State.PRED) {
			if (arg2.equals("window-time")) {
				this.currentPredictor.windowTime = Long.parseLong(value);
			} else if (arg2.equals("prediction-time")) {
				this.currentPredictor.predictionTime = Long.parseLong(value);
			} else if (arg2.equals("number-of-intervals")) {
				this.currentPredictor.numberOfIntervals = Integer
						.parseInt(value);
			} else if (arg2.equals("sleep-time")) {
				this.currentPredictor.sleepTime = Long.parseLong(value);
			} else if (arg2.equals("qos")) {
				this.currentPredictor.qosName = value;
			} else if (arg2.equals("parameter")) {
				String key = arg3.getValue("key");
				currentPredictor.parameterMap.put(key, value);
			}
		} else if (state == State.REAC) {
			if (arg2.equals("deadline")) {
				currentReactor.deadline = Long.parseLong(value);
			} else if (arg2.equals("early")) {
				currentReactor.early = Long.parseLong(value);
			} else if (arg2.equals("window-time")) {
				currentReactor.oldTime = Long.parseLong(value);
			} else if (arg2.equals("sleep-time")) {
				currentReactor.sleepTime = Long.parseLong(value);
			} else if (arg2.equals("qos")) {
				this.currentReactor.qosName = value;
			} else if (arg2.equals("parameter")) {
				String key = arg3.getValue("key");
				currentReactor.parameterMap.put(key, value);
			}
		} else if (state == State.CTRL) {
			if (arg2.equals("upper-threshold")) {
				currentController.upperThreshold = Double.parseDouble(value);
			} else if (arg2.equals("lower-threshold")) {
				currentController.lowerThreshold = Double.parseDouble(value);
			} else if (arg2.equals("time-to-scale-up")) {
				currentController.timeToScaleUp = Long.parseLong(value);
			} else if (arg2.equals("time-to-scale-down")) {
				currentController.timeToScaleDown = Long.parseLong(value);
			} else if (arg2.equals("time-to-turn-on")) {
				currentController.timeToTurnOn = Long.parseLong(value);
			} else if (arg2.equals("time-to-turn-off")) {
				currentController.timeToTurnOff = Long.parseLong(value);
			} else if (arg2.equals("listen")) {
				currentController.listenTos.add(value);
			} else if (arg2.equals("parameter")) {
				String key = arg3.getValue("key");
				currentController.parameterMap.put(key, value);
			}
		}
	}

	@Override
	public void startPrefixMapping(String arg0, String arg1)
			throws SAXException {
	}

}
